﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DNASequencing.core.data;
using DNASequencing.core.data.complex;
using DNASequencing.core.Algorithms.rating;
using DNASequencing.core.Loggers;
using DNASequencing.core.Algorithms;

namespace DNASequencing.core.Algorithms.SeqNegPos
{
    class Algorithm : AAlgorithm
    {
        //protected ComplexDT _dtForwardTree;

        //protected IRating _rRating;

        private int idleness = 0;

        private int prevDepthCut = -1;

        private static int depth = 0;

        public Algorithm(int k, int n, ComplexDT dt, IRating rating)
            : base(k, n, dt, rating)
        {
        }

        private List<Oligonucleotide> findSequence()
        {
            List<Oligonucleotide> tempResult = new List<Oligonucleotide>();
            List<Oligonucleotide> currResult = null;

            for (int i = 0; i < _oAll.Count; i++)
            {
                idleness = 0;

                //System.Console.WriteLine(i);

                currResult = copyList(tempResult);
                tempResult = new List<Oligonucleotide>();

                ++depth;

                _oAll[i].used = true;
                add(tempResult, _oAll[i]);
                _oAll[i].used = false;

                --depth;

                if (SequenceUtils.isBetterSequence(tempResult, currResult))
                {
                    currResult = tempResult;
                }
            }

            return best;
        }

        private List<Oligonucleotide> copyList(List<Oligonucleotide> toCopy)
        {
            List<Oligonucleotide> newList = new List<Oligonucleotide>();
            foreach (Oligonucleotide o in toCopy)
                newList.Add(new Oligonucleotide(o));
            return newList;
        }
        private List<Oligonucleotide> add(List<Oligonucleotide> currResult, Oligonucleotide element)
        {
            currResult.Add(element);
            Oligonucleotide[] next = null;

            next = element.getBest(k, rating);

            Oligonucleotide[] prevResult = currResult.ToArray();
            List<Oligonucleotide> tempResult = new List<Oligonucleotide>(currResult);

            foreach (Oligonucleotide o in next)
            {
                tempResult = new List<Oligonucleotide>(prevResult); // h4ck lol

                if (!o.used && SequenceUtils.sequenceLength(tempResult, o) < n)
                {
                    List<Oligonucleotide> nextResult;

                    ++depth;

                    o.used = true;
                    nextResult = add(tempResult, o);
                    o.used = false;

                    --depth;

                    if (SequenceUtils.isBetterSequence(nextResult, best))
                    {
                        //System.Console.WriteLine("BETTER");
                        //System.Console.WriteLine("PosErr: " + positiveError + "; NegErr: " + negativeError);
                        bool first = true;
                        //System.Console.Write("Not used: [");

                        List<Oligonucleotide> notUsed = SequenceUtils.getNotUsed(best, _oAll);
                        foreach (Oligonucleotide olig in notUsed)
                        {
                            if (first)
                            {
                                //System.Console.Write(olig.originalSequence);
                                first = false;
                            }
                            else
                                //System.Console.Write(", " + olig.originalSequence);

                            first = false;
                        }

                        //System.Console.WriteLine("]");

                        best = nextResult;
                        //bestNeg = SequenceUtils.extractNegativeError(best);
                        element.best = SequenceUtils.getBestFollowingSequence(element, best);
                    }

                    if (SequenceUtils.isBetterSequence(tempResult, best))
                        best = copyList(tempResult);
                }
                else
                {
                    if (depth != prevDepthCut)
                    {
                        ++idleness;
                        prevDepthCut = depth;
                    }
                }

                if (idleness >= k)
                {
                    break;
                }
            }
            return currResult;
        }

        override public AlgorithmResult process()
        {
            foreach (Oligonucleotide o in _oAll)
                o.best = new List<Oligonucleotide>();

            List<Oligonucleotide> result = findSequence();

            AlgorithmResult ar = new AlgorithmResult(result, SequenceUtils.getNotUsed(result, _oAll), negativeError, positiveError);
            //StrictLogger.printSequence(result);

            //return SequenceUtils.sequenceListToString(result);
            return ar;
        }
    }
}